
local jass = require 'jass.common'
local dbg = require 'jass.debug'
local game = require 'types.game'
local runtime = require 'jass.runtime'
local math = math
local xpcall = xpcall
local setmetatable = setmetatable
local error_handle = runtime.error_handle
local select = select
local gchash = 0
local move_index
local hit_index

local mover = class('mover')

--常量
mover.HIT_TYPE_NONE		= ''
mover.HIT_TYPE_ENEMY	= '敌人'
mover.HIT_TYPE_ALLY		= '友方'
mover.HIT_TYPE_ALL		= '别人'
mover.UNIT_ID = 'e001'                          --投射物的单位id
mover.FRAME = game.FRAME						--帧数
mover.count = 0

--全局
mover.mover_group = {}                          --无限循环
mover.removed_mover = setmetatable({}, { __mode = 'kv' })

mover.type = 'mover'                                 --类型
mover.model = nil                                    --模型路径
mover.effect = nil                                   --模型特效
mover.missile = false                                --是自动创建的投射物
mover.skill = nil                                    --关联技能
mover.damage = 0                                     --Buff附带的伤害
mover.off_angle = 0                                  --朝向偏移
mover.mover = nil                                    --移动中的单位
mover.target = nil                                   --目标
mover.block = nil                                    --触发碰撞
mover.super = nil                                    --无视边界
mover.speed = 0                                      --速度
mover.accel = 0                                      --加速度
mover.max_speed = nil                                --最大速度
mover.min_speed = nil                                --最小速度
mover.time_scale = 1                                 --运动速率
mover.moved = 0                                      --已经移动的距离
mover.max_distance = 99999                           --最大移动距离
mover.angle = nil                                    --角度
mover.face = nil                                     --朝向
mover.need_elevation = true                          --需要显示仰角
mover.move_count = 0                                 --运动计数
mover.on_move_skip = 1                               --周期回调间隔
mover.high = nil                                     --初始高度
mover.target_high = nil                              --目标高度
mover.on_move = nil                                  --每个运动周期回调函数
mover.on_hit = nil                                   --运动完成时回调
mover.on_remove = nil                                --运动结束时回调
mover.on_block = nil                                 --运动碰撞地形时回调
mover.height = nil                                   --最大高度(抛物线)
mover.height_c = 0                                   --已经变化了的高度(抛物线)
mover.height_l = 0                                   --已经变化了的高度(线性)
mover.moved_progress = 0                             --移动进度
mover.hit_type = '敌人'                              --碰撞类型
mover.hit_area = nil                                 --碰撞半径
mover.hit_same = false                               --是否重复碰撞
mover.hit_target = false                             --是否碰撞目标
mover.hit_unit = nil                                 --当前正在碰撞的单位
mover.do_reset_high = true                           --false的话就强制不还原高度，否则（true）只有非missile会还原高度 dekan
mover.paused = 0                                     --暂停

function mover:remove_path_block()
end

--移除运动方程
function mover:remove(skip_remove)
    if self.removed then
        return
    end
    self.removed = true

    --在阻挡器内移除
    self:remove_path_block()
    if self.mover.movers then
        self.mover.movers[self] = nil
    end

    --还原高度
    if not self.missile and self.do_reset_high then
        self.mover:add_high(- self.height_c - self.height_l)
    end

    if self.missile and self.model then
        self.mover:set_animation(0)
    end

    if self.on_remove then
        self:on_remove(self.mover)
    end

    if mover.mover_group[self] then
        mover.mover_group[self] = nil
        mover.count = mover.count - 1
    end

    if self.missile and not skip_remove then
        self.mover:kill()
        self.mover:removeAllEffects()
    end

    if not self.missile and self.effect then
        self.effect:remove()
    end

    mover.removed_mover[self] = self
end

--运动更新
function mover:next()
    --检查存活
    if not self.mover:is_alive() then
        self:remove()
        return
    end

    --运动方程更新
    self:next()

    if self.removed then
        return
    end

    local point = self.mover:get_point()

    self.speed = self.speed + self.accel * mover.FRAME * self.time_scale
    if self.min_speed and self.speed < self.min_speed then
        self.speed = self.min_speed
    elseif self.max_speed and self.speed > self.max_speed then
        self.speed = self.max_speed
    end

    local speed = self.speed * mover.FRAME * self.time_scale
    local height = 0
    --位移
    if not self.mover:set_position(self.next_point, not self.block, self.super) then
        if self.on_block then
            if self:on_block() then
                self:remove()
                return
            end
        elseif self.missile then
            self:remove()
            return
        end
        --求出x,y轴分速度
        local x0, y0 = point:get()
        local x1, y1 = self.next_point:get()
        --找空位
        local p1 = ac.point(x1, y0)
        local p2 = ac.point(x0, y1)
        if not p1:is_block() then
            self.mover:set_position(p1)
        elseif not p2:is_block() then
            self.mover:set_position(p2)
        end
    end

    --计算当前高度(相对值)
    self.moved = self.moved + speed
    if self.moved > self.max_distance then
        self:remove()
        return
    end

    --剩余移动进度
    local progress
    if speed >= self.distance then
        progress = 1
    else
        progress = speed / self.distance
    end

    --线性
    local target_high = self.target_high
    if self.target then
        target_high = target_high + self.target:get_high()
    end
    local height_n = (target_high - self.high) * progress
    --print('height_n', target_high, self.high, progress)
    self.high = self.high + height_n
    height = height + height_n
    self.height_l = self.height_l + height_n

    --抛物线
    if self.height then
        --全局移动进度
        local progress = (1 - self.moved_progress) * progress
        self.moved_progress = self.moved_progress + progress

        local height_n = 4 * self.height * self.moved_progress * (1 - self.moved_progress)
        height = height + height_n - self.height_c
        self.height_c = height_n
    end

    self.mover:add_high(height)

    if self.missile and speed ~= 0 and self.model and self.need_elevation then
        local r = math.atan(height, speed)
        local r = math.floor((r % 360) / 3.6)
        if r ~= self._last_animation then
            self.mover:set_animation(r)
            self._last_animation = r
        end
    end

    local function check_hit(u)
        if not self.hit_target then
            if u == self.target then
                return
            end
        end
        if not self.hit_same then
            if not self.hited_units then
                self.hited_units = {}
            end

            if self.hited_units[u] then
                return
            end

            self.hited_units[u] = true
        end
        if self:isMissile() and u:event_dispatch('单位-即将被投射物击中', u, self) then
            self:remove()
        else
            if self.on_hit and self:on_hit(u) then
               self:remove()
            end
        end
    end

    if self.hit_area and self.on_hit then
        if self.hit_area > speed then
            for _, u in self.selector
                    : in_range(self.mover, self.hit_area)
                    : ipairs() do
                check_hit(u)
                if self.removed then
                    return
                end
            end
        else
            for _, u in self.selector
                    : in_line(point, self.angle, speed + self.hit_area, self.hit_area)
                    : ipairs() do
                check_hit(u)
                if self.removed then
                    return
                end
            end
        end
    end

    self.move_count = self.move_count + 1
    if self.on_move and self.move_count % self.on_move_skip == 0 then
        self:on_move(self.mover, self.move_count)
    end
end

--初始化
function mover:init()
    gchash = gchash + 1
    dbg.gchash(self, gchash)
    self.gchash = gchash
    if not self.source then
        self.source = self.mover
    end
    if not self.start then
        self.start = self.mover or self.source
    end
    if not self.high then
        self.high = self.start[3] or 0
    end

    if self.mover then
        self.high = self.high + self.mover:get_high()
    else
        self.high = self.high + self.start:get_high()
    end
    self.selector = ac.selector():is_not(self.source)

    if self.hit_type == '敌人' then
        self.selector:is_enemy(self.source)
    elseif self.hit_type == '友方' then
        self.selector:is_ally(self.source)
    end

    return self:create()
end

--发射
function mover:launch()
    if self.mover and self.mover.type == 'unit' and (self.mover:has_restriction '禁锢' or self.mover._follow_data) then
        return
    end

    --初始化一下数据
    self:init()

    if not self.mover then
        if self.id then
            self.mover = g_playerMgr(16):create_dummy(self.id, self.start, self.face or self.angle or 0)
        else
            self.mover = g_playerMgr(16):create_dummy(mover.UNIT_ID, self.start, self.face or self.angle or 0)
        end
        self.missile = true
        if self.super == nil then
            self.super = true
        end
        jass.SetUnitBlendTime(self.mover.handle, 0)
    end

    self.mover:set_high(self.high)

    if self.skill == nil then
        print '============================'
        print(debug.traceback '运动没有关联技能!')
    end

    if self.model then
        self.effect = self.mover:add_effect('origin', self.model)
    end

    --设置缩放
    if self.size then
        self.mover:set_size(self.size)
    end

    mover.add(self)

    if not self.mover.movers then
        self.mover.movers = {}
    end
    self.mover.movers[self] = true
    if self.mover:is_pause_mover() then
        self:pause()
    end

    return self
end

--更新数据(假API)
function mover:update()
end

--是否是投射物
function mover:isMissile()
    return self.missile
end

--获得运动已经移动的距离
function mover:get_moved()
    return self.moved
end

--是否是技能弹道
function mover:is_skill()
    return self.skill
end

function mover:pause(flag)
    if flag then
        self.paused = self.paused + 1
    else
        self.paused = self.paused - 1
    end
end

--运动完成
function mover:on_finish()
	local on_finish = self.on_finish
	self.on_finish = nil
	if on_finish and on_finish(self, self.mover) then
		return
	end

	self:remove()
	return true
end

--添加进循环
function mover.add(moveInstance)
	mover.mover_group[moveInstance] = true
	mover.count = mover.count + 1
	--print('运动方程计数:', mover.count)
end

local function mover_move()
	if move_index then
		mover.mover_group[move_index] = nil
		mover.count = mover.count - 1
	end
	local tbl = {}
	for mover_data in pairs(mover.mover_group) do
		tbl[#tbl + 1] = mover_data
	end
	for i = 1, #tbl do
		move_index = tbl[i]
		if tbl[i].paused <= 0 then
			mover.next(tbl[i])
		end
	end
	move_index = nil
end

function mover.move()
	xpcall(mover_move, error_handle)
end

local function mover_hit()
	if hit_index then
		mover.mover_group[hit_index] = nil
		mover.count = mover.count - 1
	end
	local tbl = {}
	for mover_data in pairs(mover.mover_group) do
		tbl[#tbl + 1] = mover_data
	end
	for i = 1, #tbl do
		hit_index = tbl[i]
		if tbl[i].paused <= 0 then
			tbl[i]:checkHit()
		end
	end
	hit_index = nil
end

function mover.hit()
	xpcall(mover_hit, error_handle)
end

function mover.line(data)
    local lineMove = require('types.mover.line')
	setmetatable(data, lineMove)
	return data:launch()
end

function mover.target(data)
    local move_target = require 'types.mover.target'
	setmetatable(data, move_target)
	return data:launch()
end

ac.mover = mover

return mover